/*
 *                    COPYRIGHT NOTICE
 *
 *           This code is copyright protected by Freedom Inc.
 *
 *              2001-2012 All Rights Are Reserved
 */
package com.freedom.oldsong.system.dao.impl;

import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.dao.DataAccessException;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

import com.freedom.oldsong.common.OldsongNGException;
import com.freedom.oldsong.common.PaginationSupporter;
import com.freedom.oldsong.common.Table;
import com.freedom.oldsong.system.BaseDao;
import com.freedom.oldsong.system.dto.PaginationResult;
import com.ibatis.sqlmap.engine.impl.SqlMapClientImpl;
import com.ibatis.sqlmap.engine.mapping.parameter.ParameterMapping;
import com.ibatis.sqlmap.engine.mapping.statement.MappedStatement;

/**
 * @author steven.wang
 *
 * @param <T>
 */
public class BaseDaoImpl<T> extends SqlMapClientDaoSupport implements
		BaseDao<T> {

	private Class<T> pojoClass = null;
	
	private String schema = null;
	
	private String table = null;

	public void setPojoClass(Class<T> pojoClass) {
		this.pojoClass = pojoClass;
	}
	
	@SuppressWarnings("unchecked")
	public String getNameMapping() {
		if(schema==null && table == null){
			if(pojoClass==null){
				this.pojoClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
			}
			Annotation[] annotations = pojoClass.getAnnotations();

			if (annotations != null && annotations.length > 0) {
				this.table = ((Table) (annotations[0])).name();
				this.schema = ((Table) (annotations[0])).schema();
			}
		}
		return schema + "_" + table;
	}

	public int deleteByPrimaryKey(Long id)  throws DataAccessException{
		return getSqlMapClientTemplate().delete(getNameMapping() + ".deleteByPrimaryKey", id);
	}

	public Long insert(T record)  throws DataAccessException{
		return (Long) getSqlMapClientTemplate().insert(getNameMapping() + ".insert", record);
	}

	public Long insertSelective(T record)  throws DataAccessException{
		return (Long)getSqlMapClientTemplate().insert(getNameMapping() + ".insertSelective", record);
	}

	@SuppressWarnings("unchecked")
	public T selectByPrimaryKey(Long id)  throws DataAccessException{
		return (T) getSqlMapClientTemplate().queryForObject(getNameMapping() + ".selectByPrimaryKey", id);
	}

	public int updateByPrimaryKeySelective(T record) throws DataAccessException {
		return getSqlMapClientTemplate().update(getNameMapping() + ".updateByPrimaryKeySelective", record);
	}

	public int updateByPrimaryKey(T record) throws DataAccessException {
		return getSqlMapClientTemplate().update(getNameMapping() + ".updateByPrimaryKey", record);
	}
	
	@Override
	public List<?> queryForList(String queryName, Object... objects) throws DataAccessException{
		return (List<?>)getSqlMapClientTemplate().queryForList(queryName, buildParameters(queryName,objects));
	}

	@Override
	public Object queryForObject(String queryName, Object... objects) throws  DataAccessException{
		return getSqlMapClientTemplate().queryForObject(queryName, buildParameters(queryName,objects));
	}
	
	@Override
	public PaginationResult selectForPagination(String queryName,PaginationResult paginationResult, Object... objects)  throws DataAccessException{
		PaginationResult ret=paginationResult;
		if(paginationResult==null){
			ret = new PaginationResult();
		}
		
		getTotalCount(queryName, ret,objects);	
		getPaginationList(queryName,ret,objects);
		
		getSqlMapClientTemplate().getSqlMapClient().flushDataCache();
		return ret;
		
	}

	private void getPaginationList(String queryName,PaginationResult paginationResult, Object... objects){
		PaginationSupporter.checkStatement(PaginationSupporter.PAGINATION, queryName, paginationResult, (SqlMapClientImpl) this.getSqlMapClient());
		
		List<?> record = (List<?>)this.queryForList(PaginationSupporter.PAGINATION+queryName, objects);
		paginationResult.setList(record);
	}
	
	/**
	 * @param queryName
	 * @param object
	 * @param paginationResult
	 * @throws DataAccessException
	 */
	private void getTotalCount(String queryName,PaginationResult paginationResult, Object... objects) throws DataAccessException{
		PaginationSupporter.checkStatement(PaginationSupporter.COUNT, queryName, paginationResult, (SqlMapClientImpl) this.getSqlMapClient());
		
		paginationResult.setTotalCount((Integer)this.queryForObject(PaginationSupporter.COUNT + queryName, objects));
		paginationResult.setPageCount((paginationResult.getTotalCount()-1)/paginationResult.getPageSize()+1);
		if(paginationResult.getCurrentPage()>paginationResult.getPageCount()){
			paginationResult.setCurrentPage(paginationResult.getPageCount());
		}
	}
	
	/**
	 * @param objects
	 * @return
	 */
	private Object buildParameters(String queryName, Object... objects){
		if(objects==null){
			return null;
		}
		else if(objects.length==1){
			return objects[0];
		}
		else{
			MappedStatement stmt=((SqlMapClientImpl)this.getSqlMapClient()).getMappedStatement(queryName);
			ParameterMapping[] mappings = stmt.getParameterMap().getParameterMappings();
			Map<String, Object> map = new HashMap<String, Object>();
			if(mappings.length!=objects.length){
				throw new OldsongNGException("can not matche arguments");
			}
			if(mappings.length>0){
				for(int i = 0; i < mappings.length; i++){
					map.put(mappings[i].getPropertyName(), objects[i]);
				}
			}
			return map;
		}
	}
	
}
